Socket I/O Multiplexing হলো একটি প্রক্রিয়া, যা একাধিক Socket-এ I/O অপারেশন (ডেটা পাঠানো বা গ্রহণ করা) সমান্তরালে পরিচালনা করতে ব্যবহৃত হয়। এটি select(), poll(), এবং epoll() এর মতো সিস্টেম কল ব্যবহার করে Socket Programming-এ একাধিক Socket-এর I/O অপারেশন একসাথে পরিচালনা করতে সহায়ক। I/O Multiplexing নেটওয়ার্ক প্রোগ্রামিং, বিশেষত সার্ভার ডিজাইন এবং কনকারেন্ট ক্লায়েন্ট সংযোগ পরিচালনা করার জন্য অত্যন্ত গুরুত্বপূর্ণ। নিচে Socket I/O Multiplexing নিয়ে বিস্তারিত আলোচনা করা হলো।
I/O Multiplexing একটি প্রক্রিয়া, যা একাধিক Socket বা ফাইল ডেসক্রিপ্টর (FD) পর্যবেক্ষণ করে এবং যেই Socket-এ I/O অপারেশন প্রয়োজন (পাঠানো বা গ্রহণ), সেটি প্রস্তুত হলে সেই অপারেশন সম্পন্ন করে। এটি সার্ভার প্রোগ্রামিংয়ে ব্যবহৃত হয়, যেখানে একাধিক ক্লায়েন্টের সংযোগ সমান্তরালে পরিচালনা করতে হয়।
Concurrency:
Efficiency:
Non-Blocking I/O:
I/O Multiplexing সাধারণত তিনটি পদ্ধতির মাধ্যমে করা হয়: select(), poll(), এবং epoll()। এগুলোর প্রত্যেকটি বিভিন্ন পদ্ধতিতে কাজ করে এবং স্কেল এবং কর্মক্ষমতা ভিন্ন।
select()
হলো প্রাচীনতম এবং সবচেয়ে বহুল ব্যবহৃত I/O Multiplexing পদ্ধতি, যা একাধিক ফাইল ডেসক্রিপ্টর (FD) পর্যবেক্ষণ করে এবং প্রস্তুত হলে সেই ফাইল ডেসক্রিপ্টরে I/O অপারেশন করে।
সিগনেচার (C ভাষায়):
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
প্যারামিটার:
nfds
: সর্বোচ্চ ফাইল ডেসক্রিপ্টরের সংখ্যা।readfds
: পড়ার জন্য প্রস্তুত ফাইল ডেসক্রিপ্টর।writefds
: লেখার জন্য প্রস্তুত ফাইল ডেসক্রিপ্টর।exceptfds
: ব্যতিক্রম বা ত্রুটির জন্য প্রস্তুত ফাইল ডেসক্রিপ্টর।timeout
: অপেক্ষার সময় (timeout) সেট করা হয়, যা NULL
দিলে select()
অনন্তকাল অপেক্ষা করবে।নমুনা কোড (C ভাষায়):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <string.h>
int main() {
int sockfd, newsockfd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
fd_set read_fds;
char buffer[1024];
// Socket তৈরি করা
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(sockfd, 5);
while (1) {
// read_fds সেট করা
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
int max_fd = sockfd;
// select() কল করা
int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
if (activity < 0) {
perror("select() error");
}
// নতুন সংযোগ চেক করা
if (FD_ISSET(sockfd, &read_fds)) {
newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
printf("New connection accepted.\n");
// ডেটা পড়া
recv(newsockfd, buffer, sizeof(buffer), 0);
printf("Received data: %s\n", buffer);
}
}
close(sockfd);
return 0;
}
select()
একাধিক Socket পর্যবেক্ষণ করে এবং নতুন সংযোগ বা ইনকামিং ডেটা প্রস্তুত হলে সেই অনুযায়ী I/O অপারেশন সম্পন্ন করে।poll()
হলো select()
-এর একটি আধুনিক সংস্করণ, যা I/O Multiplexing আরও দক্ষতার সাথে সম্পন্ন করে এবং select()
-এর কিছু সীমাবদ্ধতা দূর করে।
সিগনেচার (C ভাষায়):
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
প্যারামিটার:
fds
: একটি pollfd
array, যা বিভিন্ন ফাইল ডেসক্রিপ্টর এবং তাদের ইভেন্ট নির্দেশ করে।nfds
: ফাইল ডেসক্রিপ্টরের সংখ্যা।timeout
: অপেক্ষার সময় (মিলিসেকেন্ডে)।নমুনা কোড (C ভাষায়):
struct pollfd fds[10];
fds[0].fd = sockfd;
fds[0].events = POLLIN; // পড়ার জন্য অপেক্ষা
int ret = poll(fds, 1, 1000);
if (ret > 0 && (fds[0].revents & POLLIN)) {
// ডেটা পড়া
}
epoll()
হলো Linux-এর জন্য উন্নত একটি I/O Multiplexing পদ্ধতি, যা স্কেলেবল এবং বড় আকারের I/O অপারেশনগুলোর জন্য কার্যকর। এটি select()
এবং poll()
-এর তুলনায় আরও দ্রুত এবং ইফিসিয়েন্ট।
সিগনেচার (C ভাষায়):
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
প্রধান ফাংশনসমূহ:
epoll_create()
: একটি epoll
ইন্সট্যান্স তৈরি করে।epoll_ctl()
: epoll
ইন্সট্যান্সে Socket যোগ করা, মুছে ফেলা, বা সংশোধন করার জন্য ব্যবহৃত হয়।epoll_wait()
: একাধিক Socket পর্যবেক্ষণ করে এবং প্রস্তুত Socket-এর জন্য অপেক্ষা করে।নমুনা কোড (C ভাষায়):
int epfd = epoll_create(1);
struct epoll_event event, events[10];
event.events = EPOLLIN;
event.data.fd = sockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
int n = epoll_wait(epfd, events, 10, 1000);
if (n > 0) {
for (int i = 0; i < n; i++) {
if (events[i].data.fd == sockfd) {
// ডেটা পড়া
}
}
}
epoll()
ব্যবহারে Socket যোগ করে এবং তাদের ইভেন্ট পর্যবেক্ষণ করে। এটি দ্রুত এবং স্কেলেবল, যা হাই-কনকারেন্সি সার্ভারগুলোর জন্য কার্যকর।কনকারেন্ট সংযোগ পরিচালনা:
Low Resource Consumption:
Non-Blocking Mode:
select()
এর সীমাবদ্ধতা:
select()
1024 ফাইল ডেসক্রিপ্টর সীমা আছে এবং এটি স্কেলেবিলিটি কমায়।poll()
এবং select()
এর কার্যকারিতা:
poll()
এবং select()
কম কার্যকর হতে পারে।প্ল্যাটফর্ম নির্ভরতা:
epoll()
কেবল Linux সিস্টেমে কাজ করে, তাই এটি অন্যান্য প্ল্যাটফর্মে বহনযোগ্য নয়।
I/O Multiplexing হলো একটি প্রক্রিয়া, যা একাধিক I/O চ্যানেল (যেমন: Socket, ফাইল ডেসক্রিপ্টর) পর্যবেক্ষণ এবং একসাথে পরিচালনা করতে ব্যবহৃত হয়। এটি একাধিক I/O অপারেশন সমান্তরালে সম্পন্ন করে, যাতে একটি প্রোগ্রাম একাধিক Socket-এ বা ফাইল ডেসক্রিপ্টরে I/O ইভেন্টের জন্য অপেক্ষা করতে পারে এবং যেই Socket বা ফাইল ডেসক্রিপ্টর প্রস্তুত (Ready) হয়, সেই অনুযায়ী কাজ করে। I/O Multiplexing সাধারণত select(), poll(), এবং epoll() এর মতো সিস্টেম কল ব্যবহার করে বাস্তবায়িত হয়।
I/O Multiplexing এমন একটি পদ্ধতি, যেখানে একটি প্রোগ্রাম একাধিক I/O চ্যানেলকে পর্যবেক্ষণ করতে পারে এবং যেই চ্যানেল প্রস্তুত হয় (যেমন: ডেটা পাঠানো বা গ্রহণ করার জন্য), সেই অনুযায়ী অপারেশন সম্পন্ন করে। এটি বিশেষত নেটওয়ার্ক প্রোগ্রামিং এবং সার্ভার ডিজাইনে ব্যবহৃত হয়, যেখানে একাধিক ক্লায়েন্ট সংযোগ সমান্তরালে পরিচালনা করতে হয়।
I/O Multiplexing ব্যবহারের প্রধান কারণ হলো একাধিক I/O চ্যানেল বা সংযোগ একসাথে পরিচালনা করা। এটি বিশেষ করে নেটওয়ার্ক সার্ভার এবং বড় স্কেল অ্যাপ্লিকেশনগুলোতে অপরিহার্য, যেখানে একাধিক ক্লায়েন্ট সংযোগ বা ডেটা অপারেশন সমান্তরালে পরিচালনা করতে হয়। নিচে I/O Multiplexing এর প্রয়োজনীয়তা নিয়ে আলোচনা করা হলো:
Concurrency এবং Scalability:
Resource Efficiency:
Low Latency:
Event-Driven Programming Support:
Non-Blocking I/O অপারেশন:
I/O Multiplexing সাধারণত তিনটি প্রধান সিস্টেম কলের মাধ্যমে পরিচালিত হয়:
select():
poll():
select()
-এর একটি আধুনিক সংস্করণ, যা একাধিক ফাইল ডেসক্রিপ্টর একসাথে পর্যবেক্ষণ করতে পারে। এটি select()
-এর কিছু সীমাবদ্ধতা দূর করে এবং ফাইল ডেসক্রিপ্টরগুলোর সাথে যুক্ত ইভেন্টগুলো পরিচালনা করে।epoll() (Linux-specific):
epoll()
ব্যবহার করা হয়, যা select()
এবং poll()
-এর তুলনায় অনেক বেশি কার্যকর এবং স্কেলেবল। এটি একাধিক সংযোগ পর্যবেক্ষণ করতে খুব কম CPU এবং মেমরি ব্যবহার করে এবং বড় স্কেল অ্যাপ্লিকেশনের জন্য উপযোগী।নিচে একটি সাধারণ উদাহরণ দেখানো হয়েছে, যেখানে Python ব্যবহার করে select()
ফাংশনের মাধ্যমে I/O Multiplexing করা হয়েছে:
import select
import socket
# একটি TCP Socket তৈরি করা
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8080))
server_socket.listen(5)
server_socket.setblocking(False)
# ফাইল ডেসক্রিপ্টরগুলোর তালিকা
sockets_list = [server_socket]
print("Server is listening on port 8080...")
while True:
# select() ব্যবহার করে I/O Multiplexing
read_sockets, _, _ = select.select(sockets_list, [], [])
for notified_socket in read_sockets:
if notified_socket == server_socket:
# নতুন সংযোগ গ্রহণ করা
client_socket, client_address = server_socket.accept()
client_socket.setblocking(False)
sockets_list.append(client_socket)
print(f"Accepted new connection from {client_address}")
else:
# ক্লায়েন্ট থেকে ডেটা পড়া
data = notified_socket.recv(1024)
if not data:
print("Connection closed.")
sockets_list.remove(notified_socket)
notified_socket.close()
else:
print(f"Received data: {data.decode()}")
select()
ব্যবহার করে একাধিক Socket পর্যবেক্ষণ করা হচ্ছে, এবং কোন Socket-এ নতুন ডেটা পাওয়া গেলে বা সংযোগ তৈরি হলে সেই অনুযায়ী কাজ করা হচ্ছে।select()
, poll()
, এবং epoll()
এর মধ্যে পার্থক্যselect()
, poll()
, এবং epoll()
হল সিস্টেম কল যা একাধিক সকার বা ফাইল ডিসক্রিপ্টর পর্যবেক্ষণ করতে ব্যবহৃত হয়। এগুলি সাধারণত I/O মাল্টিপ্লেক্সিং এর জন্য ব্যবহৃত হয়, যেখানে একটি প্রোগ্রাম একাধিক সকেট বা ফাইল ডিসক্রিপ্টরের জন্য অপেক্ষা করতে পারে এবং জানিয়ে দিতে পারে কোন ডিসক্রিপ্টরগুলি পড়তে বা লেখতে প্রস্তুত। তবে, এগুলোর কার্যকারিতা, সীমাবদ্ধতা এবং দক্ষতায় পার্থক্য রয়েছে।
নিচে এই তিনটি ফাংশনের মধ্যে পার্থক্যগুলো বিস্তারিতভাবে আলোচনা করা হলো।
select()
select()
একটি পুরনো এবং সহজ পদ্ধতি যা একাধিক ফাইল ডিসক্রিপ্টর পর্যবেক্ষণ করতে ব্যবহৃত হয়। এটি সর্বপ্রথম Unix সিস্টেমে প্রবর্তিত হয়েছিল এবং এখনো অনেক সিস্টেমে ব্যবহৃত হয়।
select()
কিছু সীমাবদ্ধতা রাখে, যেমন এটি পর্যবেক্ষণ করা ফাইল ডিসক্রিপ্টরের সংখ্যা 1024 (বা কিছু সিস্টেমে আরও কম) এর মধ্যে সীমাবদ্ধ করতে পারে।select()
যখন একাধিক ফাইল ডিসক্রিপ্টর চেক করে, তখন এটি ব্লক হয়ে যেতে পারে (অথবা নন-ব্লকিং মোডে কাজ করতে পারে), এবং যতক্ষণ না কোনো ডিসক্রিপ্টর প্রস্তুত হয়, তখন এটি কোনো কাজ করে না।select()
একটি ফাইল ডিসক্রিপ্টরের মধ্যে কোন একটি প্রস্তুত অবস্থায় থাকা চিহ্নিত করতে সক্ষম। এটি পড়ার জন্য বা লেখার জন্য প্রস্তুত ডিসক্রিপ্টরগুলির একটি সেট প্রদান করে।fd_set readfds;
struct timeval timeout;
int retval;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
retval = select(fd+1, &readfds, NULL, NULL, &timeout);
if (retval == -1) {
perror("select()");
} else if (retval) {
printf("Data is available\n");
} else {
printf("No data within 5 seconds\n");
}
poll()
poll()
হল select()
এর একটি আধুনিক সংস্করণ, যা কিছু সীমাবদ্ধতা সমাধান করে। poll()
বেশি ফাইল ডিসক্রিপ্টর পরীক্ষা করতে সক্ষম এবং এটি ফাইল ডিসক্রিপ্টরগুলির একটি লম্বা অ্যারে গ্রহণ করে। poll()
ব্যবহারের ফলে select()
এর তুলনায় আরো বেশি নমনীয়তা পাওয়া যায়।
poll()
কোনও নির্দিষ্ট সংখ্যক ফাইল ডিসক্রিপ্টর সীমাবদ্ধতা রাখে না (সেটি সিস্টেম নির্ভর হতে পারে)।poll()
ও ব্লকিং মোডে থাকতে পারে। এটি ব্যবহৃত ফাইল ডিসক্রিপ্টরগুলির মধ্যে কোনটি প্রস্তুত আছে তা নির্ধারণ করে।poll()
প্রতিটি ফাইল ডিসক্রিপ্টরের জন্য একটি pollfd
কাঠামো ব্যবহার করে, যাতে সেগুলির অবস্থা চেক করা যায়।struct pollfd fds[1];
int timeout = 5000; // 5 সেকেন্ড
fds[0].fd = fd;
fds[0].events = POLLIN;
int ret = poll(fds, 1, timeout);
if (ret == -1) {
perror("poll()");
} else if (ret) {
printf("Data is ready\n");
} else {
printf("No data within 5 seconds\n");
}
poll()
এখনও কার্যকরী নয় কারণ এটি প্রত্যেকটি ফাইল ডিসক্রিপ্টরের অবস্থা চেক করতে একটি লুপ চালাতে হয়, যা বড় আকারের অ্যারে হলে ব্যয়বহুল হয়ে যায়।epoll()
epoll()
হল একটি উন্নত এবং অত্যন্ত দক্ষ পদ্ধতি যা Linux-এ তৈরি করা হয়েছে এবং এটি বিশেষভাবে অনেক ফাইল ডিসক্রিপ্টর ম্যানেজ করার জন্য ডিজাইন করা। এটি select()
এবং poll()
থেকে অনেক দ্রুত এবং স্কেলেবল।
epoll()
কোন ফাইল ডিসক্রিপ্টর সংখ্যা সীমাবদ্ধতা রাখে না এবং এটি বড় সংখ্যক সকেট বা ফাইল ডিসক্রিপ্টরগুলির জন্য উচ্চ দক্ষতা প্রদান করে।epoll()
শুধুমাত্র প্রস্তুত থাকা ফাইল ডিসক্রিপ্টরগুলির জন্য ট্র্যাকিং করে, এবং তাই এটি অপারেশন করতে কম সময় নেয়। এর মাধ্যমে কার্যকরী সময় এবং মেমরি ব্যবহারের অপটিমাইজেশন হয়।epoll()
"এভেন্ট-ভিত্তিক" পদ্ধতি ব্যবহার করে, যেখানে সিস্টেমটি কেবলমাত্র যখন কোনো ডিসক্রিপ্টর প্রস্তুত থাকে তখনই সিগন্যাল পাঠায়।#include <sys/epoll.h>
int epoll_fd = epoll_create1(0);
struct epoll_event ev;
ev.events = EPOLLIN; // Input event (for reading)
ev.data.fd = fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
struct epoll_event events[10];
int nfds = epoll_wait(epoll_fd, events, 10, 5000); // 5 seconds timeout
if (nfds == -1) {
perror("epoll_wait()");
}
epoll()
অনেক বড় সংখ্যক সকেট বা ফাইল ডিসক্রিপ্টর পরিচালনা করতে সক্ষম এবং এটি খুব দ্রুত কাজ করে। এতে কার্যকরী সময় কমে আসে, বিশেষত বড় সিস্টেমে যেখানে অনেক সকেট থাকতে পারে।epoll()
শুধুমাত্র প্রস্তুত ডিসক্রিপ্টরগুলির সঙ্গেই কাজ করে, যার ফলে অকার্যকরী ডিসক্রিপ্টরের জন্য লুপ চালানোর প্রয়োজন নেই।বৈশিষ্ট্য | select() | poll() | epoll() |
---|---|---|---|
সীমাবদ্ধতা | 1024 ফাইল ডিসক্রিপ্টর (বা সিস্টেম নির্ভর) | কোনো নির্দিষ্ট সীমাবদ্ধতা নেই | অসীম ফাইল ডিসক্রিপ্টর সংখ্যা |
পারফরম্যান্স | বড় সংখ্যক ডিসক্রিপ্টরের জন্য কম কার্যকর | select() এর চেয়ে উন্নত, তবে বড় ডিসক্রিপ্টরের জন্য যথেষ্ট নয় | বড় ডিসক্রিপ্টর সংখ্যা এবং স্কেলেবিলিটি |
অপারেশন | প্রত্যেক ডিসক্রিপ্টরের জন্য একযোগে চেক | প্রতিটি ফাইল ডিসক্রিপ্টরের অবস্থা চেক | কেবলমাত্র প্রস্তুত ডিসক্রিপ্টরের সাথেই কাজ |
এভেন্ট ভিত্তিক | নয় | নয় | হ্যাঁ |
ব্লকিং/নন-ব্লকিং | ব্লকিং অথবা নন-ব্লকিং | ব্লকিং অথবা নন-ব্লকিং | ব্লকিং অথবা নন-ব্লকিং |
select()
হল একটি পুরনো এবং সীমাবদ্ধ পদ্ধতি, যেটি ছোট আকারের অ্যাপ্লিকেশন বা কম সংখ্যক ডিসক্রিপ্টরের জন্য উপযুক্ত।poll()
কিছুটা উন্নত, তবে এটি এখনও কার্যকরী নয় বড় আকারের সিস্টেমের জন্য।epoll()
হল সবচেয়ে দক্ষ এবং উচ্চ পারফরম্যান্সের পদ্ধতি, যা বড় সংখ্যক ডিসক্রিপ্টর পরিচালনা করতে সক্ষম এবং এটি লিনাক্সে ব্যবহৃত হয়।তবে, যদি আপনার অ্যাপ্লিকেশনটি বড় আকারের বা অনেক সকেট/ফাইল ডিসক্রিপ্টর ব্যবহার করে, তবে epoll()
হল সর্বোত্তম পছন্দ।
Socket I/O Operations হল নেটওয়ার্ক কমিউনিকেশনের একটি গুরুত্বপূর্ণ অংশ, যা ক্লায়েন্ট এবং সার্ভারের মধ্যে ডেটা পাঠানোর এবং গ্রহণ করার প্রক্রিয়া পরিচালনা করে। সঠিকভাবে এই I/O অপারেশনগুলি পরিচালনা করা না হলে নেটওয়ার্ক অ্যাপ্লিকেশনগুলির পারফরম্যান্স, নির্ভরযোগ্যতা, এবং স্কেলেবিলিটি সমস্যা তৈরি করতে পারে। Socket I/O অপারেশন ব্যবস্থাপনা সঠিকভাবে না হলে ডেটা ট্রান্সফারের সময় ব্লকিং, ডেডলক, প্যাকেট লস বা লেটেন্সি হতে পারে।
এখানে আমরা Socket I/O Operations এর ব্যবস্থাপনা সম্পর্কে আলোচনা করব, যেমন ব্লকিং এবং নন-ব্লকিং I/O, সিলেক্ট, পোলিং, ইভেন্ট-ড্রিভেন মডেল ইত্যাদি।
Blocking I/O হল এমন একটি I/O অপারেশন, যেখানে একটি সকেট যখন ডেটা পাঠানো বা গ্রহণের জন্য অপেক্ষা করে, তখন এটি পুরো থ্রেডকে ব্লক করে দেয় যতক্ষণ না ডেটা প্রাপ্ত হয়। এর ফলে একাধিক ক্লায়েন্ট একযোগে পরিচালনা করা কঠিন হয়ে পড়ে, কারণ প্রতিটি I/O অপারেশন সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে হয়।
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 65432))
server_socket.listen(1)
print("Waiting for a connection...")
client_socket, client_address = server_socket.accept() # Blocking call
print(f"Connection established with {client_address}")
data = client_socket.recv(1024) # Blocking call
print(f"Received data: {data.decode()}")
client_socket.close()
server_socket.close()
Problem: যদি recv()
বা accept()
ব্লকিং অপারেশনগুলির মধ্যে একটি থ্রেডে করা হয়, তবে সার্ভার অন্য ক্লায়েন্টের জন্য অপেক্ষা করতে বাধ্য হয়, যা পারফরম্যান্সে বাধা সৃষ্টি করে।
Non-blocking I/O হল এমন একটি I/O অপারেশন যেখানে সিস্টেম কলগুলি অবিলম্বে ফিরে আসে যদি ডেটা প্রাপ্ত না হয় বা সংযোগ প্রতিষ্ঠিত না হয়। এটি শুধুমাত্র তখন ডেটা গ্রহণ করে যখন ডেটা উপস্থিত থাকে, এবং কোনো I/O অপারেশন না থাকলে থ্রেডটি অন্য কাজ করতে পারে।
import socket
import time
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setblocking(False) # Set to non-blocking mode
server_socket.bind(('127.0.0.1', 65432))
server_socket.listen(1)
print("Waiting for a connection...")
client_socket, client_address = server_socket.accept() # Non-blocking
# Attempt to receive data (won't block if no data is available)
try:
data = client_socket.recv(1024)
if data:
print(f"Received data: {data.decode()}")
else:
print("No data received.")
except:
print("No data yet.")
client_socket.close()
server_socket.close()
Advantage: Non-blocking I/O ব্যবহার করে, একাধিক ক্লায়েন্টের সাথে একই থ্রেডে সমান্তরালভাবে কাজ করা যায়, যার ফলে পারফরম্যান্স বাড়ে এবং একাধিক সংযোগ সহজেই পরিচালনা করা যায়।
select() এবং poll() হল দুটি গুরুত্বপূর্ণ সিস্টেম কল যা একাধিক সকেট মনিটর করার জন্য ব্যবহৃত হয়। এটি একটি নির্দিষ্ট সময়ে সকেটগুলির মধ্যে কোনটি ডেটা পাঠানোর জন্য প্রস্তুত রয়েছে তা পরীক্ষা করতে সাহায্য করে, এবং ব্লকিং ছাড়াই একাধিক সংযোগ পরিচালনা করতে পারে।
select()
ফাংশন একাধিক সকেটকে একযোগভাবে মনিটর করে এবং জানিয়ে দেয় কোনটি পড়ার জন্য, লেখার জন্য, বা ত্রুটির জন্য প্রস্তুত রয়েছে।
import socket
import select
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 65432))
server_socket.listen(1)
server_socket.setblocking(False)
print("Waiting for a connection...")
inputs = [server_socket] # List of sockets to monitor
while True:
readable, _, _ = select.select(inputs, [], []) # Blocking until socket is ready
for s in readable:
if s is server_socket:
client_socket, client_address = s.accept()
print(f"Connection established with {client_address}")
inputs.append(client_socket) # Add the new client socket to the list
else:
data = s.recv(1024)
if data:
print(f"Received: {data.decode()}")
else:
inputs.remove(s)
s.close()
print("Client disconnected")
Advantage: select()
একাধিক সকেট একসাথে মনিটর করে, যাতে আপনি একই সময়ে একাধিক ক্লায়েন্টের সাথে যোগাযোগ করতে পারেন।
poll()
ফাংশন select() এর মতোই কাজ করে, তবে এটি বড় সংখ্যক সকেট ম্যানেজ করার জন্য বেশি কার্যকরী। এটি একটি ইভেন্ট ভিত্তিক সিস্টেম যেখানে আপনি নির্দিষ্ট সময়ে সকেটগুলির অবস্থা জানতে পারবেন।
import socket
import select
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 65432))
server_socket.listen(1)
server_socket.setblocking(False)
poller = select.poll()
poller.register(server_socket, select.POLLIN)
print("Waiting for a connection...")
while True:
events = poller.poll() # Polling for events
for fileno, event in events:
if fileno == server_socket.fileno():
client_socket, client_address = server_socket.accept()
print(f"Connection established with {client_address}")
poller.register(client_socket, select.POLLIN)
else:
data = fileno.recv(1024)
if data:
print(f"Received: {data.decode()}")
else:
poller.unregister(fileno)
fileno.close()
print("Client disconnected")
Advantage: poll()
বড় সংখ্যক সকেট মনিটর করতে সক্ষম এবং select()
এর চেয়ে বেশি কার্যকরী যখন অনেক সকেট ব্যবহৃত হয়।
ইভেন্ট-ড্রিভেন I/O হল এমন একটি মডেল যেখানে নির্দিষ্ট ইভেন্ট (যেমন প্যাকেট আসা, সংযোগ স্থাপন) হওয়ার জন্য আপনার থ্রেড বা প্রোগ্রাম অপেক্ষা করে। এটি সিস্টেমের অন্যান্য অংশের উপর কম লোড দেয় এবং স্কেলেবিলিটি বাড়ায়।
অ্যাসিঙ্ক্রোনাস I/O এবং I/O Multiplexing এই ধরনের ইভেন্ট-ড্রিভেন মডেলের অন্তর্গত।
import asyncio
async def handle_client(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print(f"Received {message} from {addr}")
response = f"Hello, {message}"
writer.write(response.encode())
await writer.drain()
print("Closing the connection")
writer.close()
async def main():
server = await asyncio.start_server(
handle_client, '127.0.0.1', 65432)
addr = server.sockets[0].getsockname()
print(f"Serving on {addr}")
async with server:
await server.serve_forever()
asyncio.run(main())
এখানে, asyncio লাইব্রেরি ব্যবহার করা হয়েছে যেখানে event-driven I/O প্রক্রিয়াটি অ্যালগোরিদমের মধ্যে প্রয়োগ করা হয়েছে, যাতে প্রোগ্রামটি অন্যান্য কাজ করতে পারে যখন I/O অপারেশন চলমান থাকে।
select()
ছোট স্কেল ব্যবহারের জন্য কার্যকরী এবং poll()
বড় স্কেল ব্যবহারের জন্য উপযুক্ত।্যান্স উন্নত করে।
Socket I/O Operations ব্যবস্থাপনা সার্ভার ডিজাইন এবং ক্লায়েন্ট-সার্ভার আর্কিটেকচারে একটি গুরুত্বপূর্ণ ভূমিকা পালন করে এবং এটি নেটওয়ার্ক অ্যাপ্লিকেশনের কার্যকারিতা এবং স্থিতিশীলতা নিশ্চিত করতে সহায়তা করে।
Multiplexed Server Design হল একটি নেটওয়ার্ক সার্ভারের ডিজাইন প্যাটার্ন যেখানে একাধিক ক্লায়েন্টের সাথে একযোগে যোগাযোগ পরিচালনা করা হয়, সাধারণত একটি একক থ্রেড বা প্রসেসের মাধ্যমে। এটি সিস্টেমের স্কেলেবিলিটি এবং পারফরম্যান্স বৃদ্ধির জন্য ব্যবহৃত হয়, কারণ এতে একাধিক ক্লায়েন্টের জন্য আলাদা থ্রেড তৈরি করার প্রয়োজন হয় না। এর বদলে, সিস্টেম একযোগে একাধিক সংযোগ পরিচালনা করতে select()
, poll()
, বা epoll()
ব্যবহার করে, যা সকেট I/O এর জন্য অপেক্ষা না করে একাধিক কানেকশনের সাথে কাজ করতে সাহায্য করে।
এই ডিজাইনটি সাধারণত একটি ইভেন্ট-ড্রিভেন আর্কিটেকচার, যেখানে সার্ভার শুধুমাত্র সকেট I/O এর জন্য অপেক্ষা করে এবং যেকোনো সকেট যদি প্রস্তুত থাকে (যেমন ডেটা আসা), তখন সে সকেটটির সাথে ইন্টারঅ্যাক্ট করে।
এখানে একটি Multiplexed TCP Server এর উদাহরণ দেখানো হলো যা একাধিক ক্লায়েন্টের সাথে নন-ব্লকিং I/O এবং select()
ব্যবহারের মাধ্যমে কাজ করে। এই সার্ভার একযোগে অনেক ক্লায়েন্টের সাথে সংযোগ স্থাপন এবং ডেটা গ্রহণ করে, তবে একাধিক থ্রেড বা প্রসেস ব্যবহৃত হয় না।
select()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <arpa/inet.h>
#define PORT 65432
#define MAX_CLIENTS 10
int main() {
int server_socket, client_socket, max_sd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[1024];
fd_set readfds;
// সার্ভার সকেট তৈরি
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// সার্ভারের IP এবং পোর্ট সেট করা
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// সার্ভার সকেটে বাইন্ড করা
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
// লিসেনিং শুরু করা
if (listen(server_socket, 5) == -1) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Server is listening on port %d...\n", PORT);
// `select()` ব্যবহারের জন্য সকেট সেটআপ
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
max_sd = server_socket;
// ক্লায়েন্টের সংযোগের জন্য অপেক্ষা
while (1) {
fd_set tempfds = readfds;
int activity = select(max_sd + 1, &tempfds, NULL, NULL, NULL);
if (activity == -1) {
perror("select error");
exit(EXIT_FAILURE);
}
// নতুন সংযোগ পাওয়া গেলে
if (FD_ISSET(server_socket, &tempfds)) {
client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
if (client_socket == -1) {
perror("Accept failed");
continue;
}
printf("New client connected\n");
FD_SET(client_socket, &readfds);
if (client_socket > max_sd) max_sd = client_socket;
}
// সকেট থেকে ডেটা পড়া
for (int i = 0; i <= max_sd; i++) {
if (FD_ISSET(i, &tempfds)) {
int valread = read(i, buffer, sizeof(buffer));
if (valread == 0) {
printf("Client disconnected\n");
close(i);
FD_CLR(i, &readfds);
} else {
printf("Received from client: %s\n", buffer);
send(i, "Message received", 16, 0);
}
}
}
}
close(server_socket);
return 0;
}
socket()
ফাংশন দিয়ে একটি TCP সকেট তৈরি করা হয়েছে এবং bind()
এর মাধ্যমে এটি একটি নির্দিষ্ট পোর্টে বাইন্ড করা হয়েছে।select()
ব্যবহারের জন্য সেটআপ: সার্ভার select()
ফাংশন ব্যবহার করে সকেটের উপর নজর রাখছে, এবং যখন কোনো নতুন ক্লায়েন্ট সংযোগ স্থাপন করবে, তখন সেই সকেটকে readfds
সেটে যোগ করা হবে।accept()
ফাংশন ব্যবহার করে সংযোগ গ্রহণ করা হয়।select()
ফাংশনটি একযোগে একাধিক ক্লায়েন্টের সংযোগ এবং ডেটা প্রক্রিয়া করতে সক্ষম।Multiplexed Server Design হল একটি শক্তিশালী এবং স্কেলেবল নেটওয়ার্ক আর্কিটেকচার প্যাটার্ন, যেখানে একাধিক ক্লায়েন্টকে একযোগভাবে পরিচালনা করা যায়। select()
, poll()
, বা epoll()
ফাংশন ব্যবহার করে এই ধরনের সার্ভার ডিজাইন তৈরি করা সম্ভব, যা কম রিসোর্সে অধিক কার্যকারিতা প্রদান করে এবং সিস্টেমের স্কেলেবিলিটি বাড়ায়।
common.read_more